package org.example;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

abstract class BaseDAO<T extends BaseEntity> {
    private final Class<T> entityClass;

    public BaseDAO(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    public void insert(T entity) throws Exception {
        try (Connection conn = DBUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(buildInsertSql(entity))) {

            List<Object> values = getInsertValues(entity);
            for (int i = 0; i < values.size(); i++) {
                pstmt.setObject(i + 1, values.get(i));
            }
            pstmt.executeUpdate();
        }
    }

    public T getById(String id) throws Exception {
        try (Connection conn = DBUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(buildSelectByIdSql())) {

            pstmt.setString(1, id);
            try (ResultSet rs = pstmt.executeQuery()) {
                if (rs.next()) {
                    T entity = entityClass.newInstance();
                    populateEntity(entity, rs);
                    return entity;
                }
            }
        }
        return null;
    }

    public List<T> getAll() throws Exception {
        try (Connection conn = DBUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM " + entityClass.getSimpleName().toLowerCase());
             ResultSet rs = pstmt.executeQuery()) {

            List<T> entities = new ArrayList<>();
            while (rs.next()) {
                T entity = entityClass.newInstance();
                populateEntity(entity, rs);
                entities.add(entity);
            }
            return entities;
        }
    }

    public void delete(String id) throws Exception {
        try (Connection conn = DBUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(buildDeleteSql())) {

            pstmt.setString(1, id);
            pstmt.executeUpdate();
        }
    }

    private String buildInsertSql(T entity) throws Exception {
        StringBuilder sql = new StringBuilder("INSERT INTO ");
        sql.append(entity.getTableName()).append(" (");

        List<String> columns = new ArrayList<>();
        for (Field field : entityClass.getDeclaredFields()) {
            field.setAccessible(true);
            if (field.get(entity) != null) {
                columns.add(field.getName());
            }
        }

        sql.append(String.join(", ", columns)).append(") VALUES (");
        sql.append(String.join(", ", columns.stream().map(c -> "?").collect(Collectors.toList())));
        sql.append(")");
        return sql.toString();
    }

    private List<Object> getInsertValues(T entity) throws Exception {
        List<Object> values = new ArrayList<>();
        for (Field field : entityClass.getDeclaredFields()) {
            field.setAccessible(true);
            Object value = field.get(entity);
            if (value != null) {
                values.add(value);
            }
        }
        return values;
    }

    private String buildSelectByIdSql() {
        return "SELECT * FROM " + entityClass.getSimpleName().toLowerCase() +
                " WHERE " + newInstance().getPrimaryKey() + " = ?";
    }

    private String buildDeleteSql() {
        return "DELETE FROM " + entityClass.getSimpleName().toLowerCase() +
                " WHERE " + newInstance().getPrimaryKey() + " = ?";
    }

    private T newInstance() {
        try {
            return entityClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("实体类实例化失败", e);
        }
    }

    private void populateEntity(T entity, ResultSet rs) throws Exception {
        for (Field field : entityClass.getDeclaredFields()) {
            field.setAccessible(true);
            String fieldName = field.getName();
            Class<?> fieldType = field.getType();

            if (fieldType == String.class && (fieldName.contains("time") || fieldName.contains("date"))) {
                Timestamp timestamp = rs.getTimestamp(fieldName);
                if (timestamp != null) {
                    LocalDateTime dateTime = timestamp.toLocalDateTime();
                    field.set(entity, dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                }
            } else {
                field.set(entity, rs.getObject(fieldName));
            }
        }
    }
}
